<?php
declare(strict_types=1);

namespace SixShop\Balpay;

use app\model\User;
use SixShop\Core\Helper;
use SixShop\Balpay\Entity\ExtensionBalpayLogEntity;
use SixShop\Balpay\Enum\BalpayLogTypeEnum;
use SixShop\Payment\Contracts\PaymentNotifyResult;
use SixShop\Payment\Contracts\PaymentProviderInterface;
use SixShop\Payment\Contracts\PaymentQueryResult;
use SixShop\Payment\Contracts\PaymentRefundQueryResult;
use SixShop\Payment\Contracts\PaymentRefundRequest;
use SixShop\Payment\Contracts\PaymentRefundResult;
use SixShop\Payment\Contracts\PaymentResponse;
use SixShop\Payment\Entity\ExtensionPaymentEntity;
use SixShop\Payment\Entity\ExtensionRefundEntity;
use SixShop\Payment\Enum\PaymentBizEnum;
use SixShop\Payment\Enum\PaymentStatusEnum;
use SixShop\Payment\Enum\RefundStatusEnum;
use SixShop\Payment\Event\PaymentSuccessEvent;
use SixShop\Payment\Event\RefundSuccessEvent;
use think\facade\Db;
use think\facade\Event;

class PaymentProvider implements PaymentProviderInterface
{
    const string PAYMENT_TYPE = 'balpay';

    public function __construct(
        private readonly ExtensionBalpayLogEntity $logEntity,
        private readonly ExtensionPaymentEntity   $extensionPaymentEntity,
        private readonly ExtensionRefundEntity  $extensionRefundEntity,
    )
    {
    }

    public function create(array $order, PaymentBizEnum $bizType): PaymentResponse
    {
        $this->checkPayPasswordAndBalance($order, $order['params']);
        $payment = $this->extensionPaymentEntity->where([
            'order_id' => $order['id'],
            'pay_type' => self::PAYMENT_TYPE,
            'biz_type' => $bizType,
        ])->findOrEmpty();
        if (!$payment->isEmpty()) {
            Helper::throw_logic_exception('订单已支付', status: 'balpay.order_already_paid');
        }
        $payment->transaction(function () use ($bizType, $order, $payment) {
            $payment->save([
                'user_id' => $order['user_id'],
                'order_id' => $order['id'],
                'order_sn' => $order['order_sn'],
                'biz_type' => $bizType,
                'pay_type' => self::PAYMENT_TYPE,
                'amount' => $order['pay_amount'],
                'status' => PaymentStatusEnum::SUCCESS,
            ]);
            $this->logEntity->change(
                $order['user_id'],
                (float)$order['pay_amount'],
                BalpayLogTypeEnum::CONSUMTION,
                '支付订单:' . $order['order_sn'],
                $order['id']
            );

        });
        // 余额支付直接成功
        Event::trigger(new PaymentSuccessEvent($order['order_sn'], self::PAYMENT_TYPE, $payment->toArray(), $bizType));
        return new PaymentResponse($order['order_sn'], self::PAYMENT_TYPE, $payment->toArray());
    }

    public function notify(array $request): PaymentNotifyResult
    {
        throw new \Exception('未实现notify方法');

    }

    public function query(int $recordID): PaymentQueryResult
    {
        throw new \Exception('未实现query方法');

    }

    public function refund(int $recordID, PaymentRefundRequest $param): PaymentRefundResult
    {
        $payment = $this->extensionPaymentEntity->find($recordID);
        $reund = Db::transaction(function () use ($payment, $param) {
            $refund = $this->extensionRefundEntity->create([
                'payment_id' => $payment->id,
                'order_sn' => $payment->out_trade_no,
                'reason' => $param->getReason(),
                'amount' => $param->getAmount(),
                'status' => RefundStatusEnum::SUCCESS,
                'refund_param' => $param->getRaw(),
                'success_time' => time(),
                'status_desc' => '已经退款到用户余额',
            ]);
            $this->logEntity->change(
                $payment->user_id,
                $param->getAmount(),
                BalpayLogTypeEnum::REFUND,
                sprintf('订单退款成功，订单编号：%s', $payment->order_sn),
                $payment->order_id
            );
            // 余额退款直接成功
            Event::trigger(new RefundSuccessEvent($refund, $payment->model(), $param));
            return $refund;
        });

        return new PaymentRefundResult($reund);
    }

    public function refundQuery(int $refundID): PaymentRefundResult
    {
        throw new \Exception('未实现refundQuery方法');
    }

    private function checkPayPasswordAndBalance(array $order, array $params): void
    {
        $password = $params['pay_password'] ?? '';
        if (empty($password)) {
            Helper::throw_logic_exception('请输入支付密码', status: 'balpay.pay_password_empty');
        }
        $user = User::find($order['user_id']);
        if (empty($user->pay_password)) {
            Helper::throw_logic_exception('请先设置支付密码', status: 'balpay.pay_password_not_set');
        }
        if (!password_verify($password, $user->pay_password)) {
            Helper::throw_logic_exception('支付密码错误', status: 'balpay.pay_password_error');
        }
    }
}